home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer)…68k, x86, SPARC, PA-RISC] / NeXTSTEP 3.3 Dev Intel.iso / NextDeveloper / Examples / AppKit / ImageFilter / iffToTiff.m next >
Encoding:
Text File  |  1995-02-06  |  12.4 KB  |  436 lines

  1. /*
  2.  * iffToTiff.m, IFF to TIFF converter. Somewhat incomplete.
  3.  * Author: Ali T. Ozer, NeXT Computer, Inc.
  4.  * Written for 3.0, June 2, 1992.
  5.  *
  6.  * You may freely copy, distribute and reuse the code in this example.
  7.  * NeXT disclaims any warranty of any kind, expressed or implied, as to its
  8.  * fitness for any particular use.
  9.  */
  10.  
  11. #import <appkit/appkit.h>
  12. #import <libc.h>
  13.  
  14. /* IFF related structures */
  15.  
  16. typedef struct {
  17.     char chunkID[4];
  18.     unsigned int chunkSize;
  19. } ChunkHeader;
  20.  
  21. typedef struct {
  22.     short w, h, x, y;
  23.     char  nPlanes, masking, compression, pad1;
  24.     short transparentColor;
  25.     char  xAspect, yAspect;
  26.     short pageWidth, pageHeight;
  27. } BitMapHeader;
  28.  
  29. typedef struct {
  30.     BitMapHeader    bmhd;        /* From BMHD chunk */
  31.     unsigned long   *colorTable;    /* Each entry 32 bits; 00rrggbb */
  32.     unsigned short  colorCount;        /* Number of colors read in from CMAP; upto 256 */
  33.     unsigned char   *imageData;        /* Image data */
  34.     unsigned long   viewMode;        /* CAMG chunk */
  35. } ILBMInfo;
  36.  
  37. /* Various IFF bits. */
  38.  
  39. #define    HIRES            0x8000
  40. #define    LACE            0x0004
  41. #define    HAM            0x0800
  42. #define    EXTRA_HALFBRITE        0x0080
  43.  
  44. #define MASKNONE        0
  45. #define MASKEXPLICIT        1
  46. #define MASKTRANSPARENTCOLOR    2
  47. #define MASKLASSO        3
  48.  
  49. #define CMPNONE            0
  50. #define CMPBYTERUN1        1
  51.  
  52. static inline unsigned char getByte (NXStream *stream)
  53. {
  54.     return (unsigned char)NXGetc(stream);    
  55. }
  56.  
  57. static void getBytes (NXStream *stream, unsigned char *buf, int count)
  58. {
  59.     NXRead(stream, buf, count);    
  60. }
  61.  
  62. static inline unsigned short getShort (NXStream *stream)
  63. {
  64.     return (((unsigned short)getByte(stream)) << 8) + getByte(stream);
  65. }
  66.  
  67. static inline unsigned long getLong (NXStream *stream)
  68. {
  69.     return (((unsigned long)getShort(stream)) << 16) + getShort(stream);
  70. }
  71.  
  72. static void getBMHD (NXStream *stream, BitMapHeader *bmhd)
  73. {
  74.     bmhd->w = getShort(stream);
  75.     bmhd->h = getShort(stream);
  76.     bmhd->x = getShort(stream);
  77.     bmhd->y = getShort(stream);
  78.     bmhd->nPlanes = getByte(stream);
  79.     bmhd->masking = getByte(stream);
  80.     bmhd->compression = getByte(stream);
  81.     bmhd->pad1 = getByte(stream);
  82.     bmhd->transparentColor = getShort(stream);
  83.     bmhd->xAspect = getByte(stream);
  84.     bmhd->yAspect = getByte(stream);
  85.     bmhd->pageWidth = getShort(stream);
  86.     bmhd->pageHeight = getShort(stream);
  87. }
  88.  
  89. static void getID (NXStream *stream, char str[4])
  90. {
  91.     str[0] = getByte(stream);
  92.     str[1] = getByte(stream);
  93.     str[2] = getByte(stream);
  94.     str[3] = getByte(stream);
  95. }
  96.  
  97. static void getHeader (NXStream *stream, ChunkHeader *header)
  98. {
  99.     getID (stream, header->chunkID);
  100.     header->chunkSize = getLong(stream);
  101. }
  102.  
  103. static BOOL readILBM (NXStream *stream, ILBMInfo *pic)
  104. {
  105.     ChunkHeader header;
  106.     
  107.     bzero (pic, sizeof(ILBMInfo));
  108.  
  109.     getHeader (stream, &header);
  110.     if (strncmp(header.chunkID, "FORM", 4)) return 0;
  111.     
  112.     getID (stream, header.chunkID);
  113.     if (strncmp(header.chunkID, "ILBM", 4)) return 0;
  114.     
  115.     // Read chunks until we get to the body chunk
  116.     
  117.     while (!NXAtEOS(stream)) {
  118.  
  119.     getHeader (stream, &header);
  120.  
  121.     if (strncmp(header.chunkID, "BODY", 4) == 0) {
  122.         pic->imageData = (unsigned char *)malloc(header.chunkSize);
  123.         getBytes (stream, pic->imageData, header.chunkSize);
  124.         return 1;    // Done, get out of here...
  125.     } else if (strncmp(header.chunkID, "BMHD", 4) == 0) {
  126.         getBMHD (stream, &(pic->bmhd));
  127.     } else if (strncmp(header.chunkID, "CMAP", 4) == 0) {
  128.         int cnt;
  129.         BOOL oldStyleImage = YES;
  130.         pic->colorCount = (unsigned int)(header.chunkSize/3);
  131.         pic->colorTable = (unsigned long *)malloc(pic->colorCount * sizeof(long));
  132.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  133.         unsigned char r, g, b;
  134.         r = getByte(stream);
  135.         g = getByte(stream);
  136.         b = getByte(stream);
  137.         pic->colorTable[cnt] = (((unsigned long)r) << 16) | (((unsigned long)g) << 8) | (((unsigned long)b));
  138.         }
  139.         if (pic->colorCount & 1) NXSeek (stream, 1, NX_FROMCURRENT);    /* Align... */
  140.         /* Check to see if this is an old-style image */
  141.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  142.         if (((pic->colorTable[cnt] & 0x000f0f0f) != 0) && (pic->colorTable[cnt] != 0x00ffffff)) {
  143.             oldStyleImage = NO;
  144.             break;
  145.         }
  146.         }
  147.         if (oldStyleImage) {
  148.         for (cnt = 0; cnt < pic->colorCount; cnt++) {
  149.             pic->colorTable[cnt] |= ((pic->colorTable[cnt] & 0x00f0f0f0) >> 4);
  150.         }
  151.         }
  152.     } else if (strncmp(header.chunkID, "CAMG", 4) == 0) {
  153.         pic->viewMode = getLong(stream);
  154.     } else {
  155.         // Ignore this chunk
  156.         NXSeek (stream, header.chunkSize + ((header.chunkSize & 1) ? 1 : 0), NX_FROMCURRENT);
  157.     }
  158.     }
  159.  
  160.     return 0;
  161. }      
  162.  
  163. static unsigned char *expandIFFBody (BitMapHeader  *bmhd, unsigned char *sourceBuf)
  164. {
  165.     signed char n;
  166.     unsigned char *start, *cur, *destBuf;
  167.     short lineLen, plane, i, numPlanes, rowBytes;
  168.  
  169.     numPlanes = (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0));
  170.     lineLen = (bmhd->w + 7) / 8;
  171.     destBuf = (unsigned char *)malloc(lineLen * bmhd->h * (bmhd->nPlanes + ((bmhd->masking == MASKEXPLICIT) ? 1 : 0)));
  172.  
  173.     start = sourceBuf;
  174.     cur = destBuf;
  175.  
  176.     for (i = 0; i < bmhd->h; i++) {
  177.     for (plane = 0; plane < numPlanes; plane++) { /* n planes/line */
  178.         if (bmhd->compression == CMPBYTERUN1) { /* compressed */
  179.         rowBytes = lineLen;
  180.         while (rowBytes) { /* unpack until 1 scan-line complete */
  181.             n = *sourceBuf++; /* fetch block run marker */    
  182.             if (n >= 0) {
  183.             int move = (++n > rowBytes) ? rowBytes : n;
  184.             memmove (cur, sourceBuf, n);
  185.             rowBytes -= move;
  186.             cur += move;
  187.             sourceBuf+=n;
  188.             } else { /* Compressed block */
  189.             n = -n+1;
  190.             if (n > rowBytes) {n = rowBytes;}
  191.             rowBytes -= n;
  192.             memset (cur, (unsigned int)*sourceBuf++, (unsigned int)n);
  193.             cur += n;
  194.             }
  195.     
  196.         }
  197.         } else { /* uncompressed */
  198.         memmove (cur, sourceBuf, (unsigned int)lineLen);
  199.         sourceBuf += lineLen;
  200.         cur += lineLen;
  201.         }
  202.     }
  203.     if ((bmhd->compression == CMPNONE) && ((sourceBuf - start) & 1)){
  204.         sourceBuf++;    /* Each scanline should be in increments of 2-bytes wide */
  205.     }
  206.     }
  207.     
  208.     return destBuf;
  209. }
  210.  
  211. NXBitmapImageRep *convertIFFToTIFF (NXStream *stream)
  212. {
  213.     ILBMInfo pic;
  214.     unsigned char *tiffData, *iffData;
  215.     unsigned char mask[8] = {128,64,32,16,8,4,2,1};
  216.     int spp, bps, scrw, scrh, scrd, scrc, actuald, cnt;
  217.     int readMask = 0;            // Read transparency if provided?
  218.     BOOL adjustAspectRatio = YES;    // Set resolution so that the aspect ratio is correct?
  219.     BOOL guessAspectRatio = YES;    // Attempt to make a guess as to what the correct aspect ratio is?
  220.     NXSize tiffSize = {0.0, 0.0};
  221.     NXBitmapImageRep *tiff = nil;
  222.     
  223.     if (!readILBM (stream, &pic)) {
  224.     return nil;
  225.     }
  226.  
  227.     scrw = pic.bmhd.w;         /* Screen width in bits */
  228.     scrh = pic.bmhd.h;         /* Screen height in scanlines */
  229.     scrd = pic.bmhd.nPlanes;   /* Screen depth in bit planes */
  230.     actuald = scrd + ((pic.bmhd.masking == MASKEXPLICIT) ? 1 : 0);
  231.     scrc = pic.colorCount;     /* Screen colors in # of color registers */
  232.     
  233.     if (scrd > 8) {
  234.     return nil;
  235.     }
  236.   
  237.     /* Uncompress the IFF image */
  238.     
  239.     iffData = expandIFFBody (&pic.bmhd, pic.imageData);
  240.     free (pic.imageData);
  241.     pic.imageData = NULL;
  242.     
  243.     if (guessAspectRatio && adjustAspectRatio) {
  244.     int xGuess;
  245.     float aspect = (pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 0.0;
  246.     if ((pic.viewMode & HIRES) && !(pic.viewMode & LACE)) {
  247.         xGuess = 5;
  248.     } else if (!(pic.viewMode & HIRES) && (pic.viewMode & LACE)) {
  249.         xGuess = 20;
  250.     } else {
  251.         xGuess = 10;
  252.     }
  253.     if (fabs((((float)xGuess) / 11.0) - aspect) > 0.001) {    // Might be wrong; fix it up...
  254.         pic.bmhd.xAspect = xGuess;
  255.         pic.bmhd.yAspect = 11;
  256.     }
  257.     }
  258.  
  259.     if (tiffSize.width < 1 || tiffSize.height < 1) {
  260.     int realWidth, realHeight;
  261.     float aspect = (adjustAspectRatio && pic.bmhd.yAspect && pic.bmhd.xAspect) ? (((float)pic.bmhd.xAspect) / ((float)pic.bmhd.yAspect)) : 1.0;
  262.     if (adjustAspectRatio) {
  263.         realWidth = (aspect > 1.0) ? scrw : (scrw * aspect);
  264.         realHeight = (aspect < 1.0) ? scrh : (scrh / aspect);
  265.     } else {
  266.         realWidth = (aspect < 1.0) ? scrw : (scrw * aspect);
  267.         realHeight = (aspect > 1.0) ? scrh : (scrh / aspect);
  268.     }
  269.     if ((tiffSize.width < 1) && (tiffSize.height < 1)) {
  270.         tiffSize.width = realWidth;
  271.         tiffSize.height = realHeight;
  272.     } else if (tiffSize.width < 1) {
  273.         tiffSize.width = tiffSize.height * realWidth / realHeight;
  274.     } else {
  275.         tiffSize.height = tiffSize.width * realHeight / realWidth;
  276.     }
  277.     tiffSize.width = MAX(tiffSize.width, 1);
  278.     tiffSize.height = MAX(tiffSize.height, 1);
  279.     }
  280.  
  281.     {
  282.     unsigned char *scanLines[8], curMask;
  283.     unsigned short alpha;
  284.     unsigned long color;
  285.     int w, h, cnt, rshift, byte, reg;
  286.     int rowBytes = ((scrw + 7) / 8);
  287.     int ham, halfbrite;
  288.     
  289.     if (readMask && (pic.bmhd.masking == MASKEXPLICIT || pic.bmhd.masking == MASKTRANSPARENTCOLOR)) {
  290.         readMask = pic.bmhd.masking;
  291.     } else {
  292.         readMask = 0;
  293.     }
  294.         
  295.     halfbrite = pic.viewMode & EXTRA_HALFBRITE;
  296.     if (ham = ((pic.viewMode & HAM) != 0)) {
  297.         spp = 3;
  298.     } else {
  299.         spp = 1;
  300.         /* is the image grayscale? (for all colors in the palette, r == g == b?) */
  301.         for (cnt = 0; cnt < pic.colorCount; cnt++) {
  302.         color = pic.colorTable[cnt];
  303.         if ((((color >> 16) & 255) != (color & 255)) || (((color >> 16) & 255) != ((color >> 8) & 255))) {
  304.             spp = 3;
  305.             break;
  306.         }
  307.         }
  308.     }
  309.     spp += (readMask ? 1 : 0);
  310.     
  311.     bps = 4;
  312.     /* can the image be represented in 4-bits? */
  313.     for (cnt = 0; cnt < pic.colorCount; cnt++) {
  314.         color = pic.colorTable[cnt];
  315.         if ((pic.colorTable[cnt] & 0x00f0f0f0) != ((pic.colorTable[cnt] << 4) & 0x00f0f0f0)) {
  316.         bps = 8;
  317.         break;
  318.         }
  319.     }
  320.         
  321.     tiff = [[NXBitmapImageRep alloc] initData:NULL
  322.                         pixelsWide:scrw
  323.                         pixelsHigh:scrh
  324.                         bitsPerSample:bps
  325.                         samplesPerPixel:spp
  326.                         hasAlpha:((readMask != 0) ? YES : NO)
  327.                         isPlanar:NO
  328.                         colorSpace:(spp > 2) ? NX_RGBColorSpace : NX_OneIsWhiteColorSpace
  329.                         bytesPerRow:0
  330.                         bitsPerPixel:0];
  331.     [tiff setSize:&tiffSize];
  332.     tiffData = [tiff data];
  333.     
  334.     alpha = (bps == 8) ? 0x0ff : 0x0f;
  335.         
  336.     for (h = 0; h < scrh; h++) {
  337.     
  338.         for (cnt = 0; cnt < scrd; cnt++) {
  339.         scanLines[cnt] = iffData + rowBytes * ((actuald * h) + cnt);
  340.         }
  341.         
  342.         for (w = 0; w < scrw; w++) {
  343.         curMask = mask[w & 7];
  344.         rshift = 7 - (w & 7);
  345.         byte = w >> 3;
  346.         reg = 0;
  347.         for (cnt = 0; cnt < scrd; cnt++) {
  348.             reg += ((((*(scanLines[cnt] + byte)) & curMask) >> rshift) << cnt);
  349.         }
  350.         switch (readMask) {
  351.             case MASKNONE:
  352.             break;
  353.             case MASKEXPLICIT: 
  354.             if ((*(iffData + rowBytes * ((actuald * h) + scrd) + byte)) & curMask) {
  355.                 alpha = (bps == 8) ? 0x0ff : 0x0f;
  356.             } else {
  357.                 alpha = 0;
  358.             }
  359.             break;
  360.             case MASKTRANSPARENTCOLOR: 
  361.             if (reg == pic.bmhd.transparentColor) {
  362.                 alpha = 0;
  363.             } else {
  364.                 alpha = (bps == 8) ? 0x0ff : 0x0f;
  365.             }
  366.             break;
  367.         }
  368.     
  369.         if (ham) {
  370.             int regf = reg & 0x0f;
  371.             if (w == 0) {
  372.             color = pic.colorTable[0];
  373.             }
  374.             switch (reg & 0x030) {
  375.             case 0x000: color = pic.colorTable[reg]; break;
  376.             case 0x010: color = (color & 0x00ffff00) | (regf | (regf << 4)); break;
  377.             case 0x030: color = (color & 0x00ff00ff) | ((regf | (regf << 4)) << 8); break;  
  378.             case 0x020: color = (color & 0x0000ffff) | ((regf | (regf << 4)) << 16); break;  
  379.             }
  380.         } else if (halfbrite && (reg >= 32)) {
  381.             color = ((pic.colorTable[reg % 32]) >> 1) & 0x007f7f7f;
  382.         } else {
  383.             color = pic.colorTable[reg];
  384.         }
  385.     
  386.         if (!alpha) color = 0;
  387.     
  388.         if (bps == 8) {
  389.             if (spp == 1 || spp == 2) {
  390.             *tiffData++ = color & 0x0ff;
  391.             if (spp == 2) *tiffData++ = alpha;
  392.             } else if (spp == 3 || spp == 4) {
  393.             *tiffData++ = (color >> 16) & 0x0ff;
  394.             *tiffData++ = (color >> 8) & 0x0ff;
  395.             *tiffData++ = (color & 0x0ff);
  396.             if (spp == 4) *tiffData++ = alpha;
  397.             }
  398.         } else {    /* bps == 4 */
  399.             switch (spp) {
  400.             case 1:
  401.             if (w & 1) {    /* odd pixel */
  402.                 *tiffData |= (color & 0x0f);
  403.                 tiffData++;
  404.             } else {        /* even pixel */
  405.                 *tiffData = (color & 0x0f0);
  406.             }
  407.             break;
  408.             case 2:
  409.             *tiffData++ = (color & 0x0f0) | alpha;
  410.             break;
  411.             case 3:
  412.             if (w & 1) {    /* odd pixel */
  413.                 *tiffData |= (color >> 16) & 0x0f; /* the red */
  414.                 tiffData++;
  415.                 *tiffData++ = ((color >> 8) & 0xf0) | (color & 0x0f); /* green & blue */
  416.             } else {        /* even pixel */
  417.                 *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
  418.                 *tiffData = (color & 0x0f0);
  419.             }
  420.             break;
  421.             case 4:
  422.             *tiffData++ = ((color >> 16) & 0xf0) | ((color >> 8) & 0x0f);
  423.             *tiffData++ = (color & 0x0f0) | alpha;
  424.             }
  425.         }
  426.         }
  427.     
  428.         if ((spp & 1) && (bps == 4) && (scrw & 1)) tiffData++;    /* We're stuck in mid byte! */
  429.     
  430.     }
  431.     }
  432.  
  433.     free(iffData);
  434.  
  435.     return tiff;
  436. }